package org.eesgmbh.gimv.samples.googlechartapi.client;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

import org.eesgmbh.gimv.client.event.LoadImageDataEvent;
import org.eesgmbh.gimv.client.event.LoadImageDataEventHandler;
import org.eesgmbh.gimv.client.event.SetBoundsEvent;
import org.eesgmbh.gimv.client.event.SetBoundsEventHandler;
import org.eesgmbh.gimv.client.event.SetImageUrlEvent;
import org.eesgmbh.gimv.client.event.SetInnerPixelBoundsEvent;
import org.eesgmbh.gimv.client.event.SetMaxBoundsEvent;
import org.eesgmbh.gimv.client.event.SetViewportBoundsEvent;
import org.eesgmbh.gimv.client.history.DefaultSetBoundsEventHistoryTokenTransformer;
import org.eesgmbh.gimv.client.history.UnparsableHistoryTokenException;
import org.eesgmbh.gimv.client.widgets.Viewport;
import org.eesgmbh.gimv.shared.util.Bounds;
import org.eesgmbh.gimv.shared.util.DateUtils;

import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;

/**
 * TODO: this is extremely prototypical
 *
 * @author Christian Seewald - EES GmbH - c.seewald@ees-gmbh.de
 *
 */
@SuppressWarnings("deprecation")
public class GoogleChartApiSampleController {

	private final Viewport viewport;
	private final HandlerManager handlerManager;

	private final AppEventBroker eventBroker;
	private final HistoryManager historyManager;

	private Date currentStart = null;
	private Date currentEnd = null;
	private double currentTop;
	private double currentBottom;

	public GoogleChartApiSampleController(Viewport viewport, HandlerManager handlerManager) {
		this.viewport = viewport;

		this.handlerManager = handlerManager;
		this.eventBroker = new AppEventBroker();
		this.historyManager = new HistoryManager();

		handlerManager.addHandler(LoadImageDataEvent.TYPE, this.eventBroker);
		handlerManager.addHandler(SetBoundsEvent.TYPE, this.eventBroker);

		History.addValueChangeHandler(this.historyManager);
	}

	public void initApp() {
		this.eventBroker.setViewportBounds();

		if (!"".equals(History.getToken())) { //has history items
			History.fireCurrentHistoryState();

		} else {
			//Initialize to some defaults
			List<Date> dataSetDates = sampleDataSetDatesAsList();
			Bounds sampleDataSetValueRange = sampleDataSetValueRange();

			this.currentStart = dataSetDates.get(2);
			this.currentEnd = dataSetDates.get(dataSetDates.size() - 3);
			this.currentBottom = sampleDataSetValueRange.shiftBottomProportionally(0.20).getBottom();
			this.currentTop = sampleDataSetValueRange.shiftTopProportionally(-0.20).getTop();

			handlerManager.fireEvent(new LoadImageDataEvent());
		}

	}

	private class AppEventBroker implements LoadImageDataEventHandler, SetBoundsEventHandler {

		public void onLoadImageData(LoadImageDataEvent event) {
			handlerManager.fireEvent(new SetImageUrlEvent(createUrl(new Bounds(currentStart.getTime(), currentEnd.getTime(), (long)currentTop, (long)currentBottom))));

			handlerManager.fireEvent(new SetInnerPixelBoundsEvent(new Bounds(39, 494, 29, 231)));

			handlerManager.fireEvent(new SetMaxBoundsEvent(new Bounds(
					DateUtils.truncateTime(SAMPLE_DATA.firstKey()).getTime(),
					DateUtils.ceilTime(SAMPLE_DATA.lastKey()).getTime(), null, null)));

			SetBoundsEvent setBoundsEvent = new SetBoundsEvent(new Bounds(
					(double) currentStart.getTime(),
					(double) currentEnd.getTime(),
					currentTop,
					currentBottom
			), AppEventBroker.this);

			handlerManager.fireEvent(setBoundsEvent);

			History.newItem("bounds=" + DefaultSetBoundsEventHistoryTokenTransformer.toHistoryTokenValue(setBoundsEvent), false);
		}

		public void onSetBounds(SetBoundsEvent event) {
			currentStart = DateUtils.truncateTime(new Date(event.getBounds().getLeft().longValue()));
			currentEnd = DateUtils.truncateTime(new Date(event.getBounds().getRight().longValue()));
			currentTop = event.getBounds().getTop();
			currentBottom = event.getBounds().getBottom();
		}

		public void setViewportBounds() {
			handlerManager.fireEvent(new SetViewportBoundsEvent(getViewportBounds()));
		}
	}

	private Bounds getViewportBounds() {
		return new Bounds(0, viewport.getOffsetWidth(), 0, viewport.getOffsetHeight());
	}

	private List<Date> sampleDataSetDatesAsList() {
		return new ArrayList<Date>(SAMPLE_DATA.keySet());
	}

	private Bounds sampleDataSetValueRange() {
		Integer minY = Integer.MAX_VALUE;
		Integer maxY = Integer.MIN_VALUE;

		for (Date date : SAMPLE_DATA.keySet()) {
			minY = Math.min(SAMPLE_DATA.get(date), minY);
			maxY = Math.max(SAMPLE_DATA.get(date), maxY);
		}

		return new Bounds(null, null, minY, maxY);
	}

	private String createUrl(Bounds bounds) {
		String url = "http://chart.apis.google.com/chart?chs=" + getViewportBounds().getAbsWidth().intValue() + "x" + getViewportBounds().getAbsHeight().intValue() + "&cht=lxy&chtt=A%20Sample%20Google%20Chart&chxt=x,y&" +
				"chxr=0,#minX#,#maxX#,1|1,#minY#,#maxY#,5&" +
				"chds=#minX#,#maxX#,#minY#,#maxY#&" +
				"chd=t:#xValues#|#yValues#";

		String xValues = "";
		String yValues = "";

		Date minX = null;
		Date maxX = null;
		Integer minY = Integer.MAX_VALUE;
		Integer maxY = Integer.MIN_VALUE;

		for (Date date : SAMPLE_DATA.keySet()) {
			if (!bounds.isHorizontalBoundsDefined() || bounds.containsHorizontally(date.getTime())) {
				xValues += date.getDate();
				yValues += SAMPLE_DATA.get(date);

				xValues += ",";
				yValues += ",";

				minY = Math.min(SAMPLE_DATA.get(date), minY);
				maxY = Math.max(SAMPLE_DATA.get(date), maxY);

				if (minX == null || date.before(minX)) {
					minX = date;
				}
				if (maxX == null || date.after(maxX)) {
					maxX = date;
				}
			}
		}

		xValues = xValues.substring(0, xValues.length()-1);
		yValues = yValues.substring(0, yValues.length()-1);

		url = url.replaceAll("#xValues#", xValues);
		url = url.replaceAll("#yValues#", yValues);
		url = url.replaceAll("#minX#", String.valueOf(minX.getDate()));
		url = url.replaceAll("#maxX#", String.valueOf(maxX.getDate()));
		url = url.replaceAll("#minY#", String.valueOf(bounds.getBottom()));
		url = url.replaceAll("#maxY#", String.valueOf(bounds.getTop()));

		return url;
	}

	private final static SortedMap<Date, Integer> SAMPLE_DATA = new TreeMap<Date, Integer>();
	static {
		SAMPLE_DATA.put(new Date(110, 1, 3), 540);
		SAMPLE_DATA.put(new Date(110, 1, 4), 542);
		SAMPLE_DATA.put(new Date(110, 1, 5), 539);
		SAMPLE_DATA.put(new Date(110, 1, 6), 536);
		SAMPLE_DATA.put(new Date(110, 1, 7), 538);
		SAMPLE_DATA.put(new Date(110, 1, 8), 541);
		SAMPLE_DATA.put(new Date(110, 1, 9), 543);
		SAMPLE_DATA.put(new Date(110, 1, 10), 544);
		SAMPLE_DATA.put(new Date(110, 1, 11), 551);
		SAMPLE_DATA.put(new Date(110, 1, 12), 555);
		SAMPLE_DATA.put(new Date(110, 1, 13), 552);
		SAMPLE_DATA.put(new Date(110, 1, 14), 549);
		SAMPLE_DATA.put(new Date(110, 1, 15), 545);
		SAMPLE_DATA.put(new Date(110, 1, 16), 548);
		SAMPLE_DATA.put(new Date(110, 1, 17), 552);
	}

	private class HistoryManager implements ValueChangeHandler<String> {

		public void onValueChange(ValueChangeEvent<String> event) {

			if (event.getValue().contains("bounds=")) {
				String boundsHistoryTokenValue = event.getValue().substring(event.getValue().indexOf("bounds=") + "bounds=".length());

				try {
					handlerManager.fireEvent(DefaultSetBoundsEventHistoryTokenTransformer.toEvent(boundsHistoryTokenValue));
					handlerManager.fireEvent(new LoadImageDataEvent());
				} catch (UnparsableHistoryTokenException e) {
					e.printStackTrace();

					Window.alert(e.getMessage());
				}
			}
		}
	}
}
